From 56a7a7851b09cb30a5cd543c8cb4f926109b4290 Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.net>
Date: Sun, 24 May 2026 13:22:34 +0000
Subject: refactor(locale): move hardcoded UI strings into english locale

Adds optional namespaces (common, errors, commandPalette, headTitle,
notifications, schedule, events, home, reader, routes, badgePreview,
badgeWall) and extends existing ones (settings.*, lists.*, tools.*,
user.*, hololive.*) on the Locale interface. New fields are optional
so japanese.ts can omit them; svelte-i18n's fallbackLocale handles
the runtime miss.

HeadTitle gains an optional routeKey prop for type-safe lookup.
defaultActions becomes a factory so the command palette re-reads
locale on language toggle. The existing JP feedback translation
in routes/settings is preserved via japanese.ts.

Out of scope (kept hardcoded): service-worker.ts, app.html,
Landing*.svelte, tools.ts registry, Easter Event 2025 pages.
---
 src/routes/user/[user]/badges/+page.svelte | 103 ++++++++++++++++-------------
 1 file changed, 56 insertions(+), 47 deletions(-)

(limited to 'src/routes/user/[user]/badges/+page.svelte')

diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index 9ff81118..de55b456 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -12,6 +12,7 @@ import {
 } from "$lib/Utility/time";
 import proxy from "$lib/Utility/proxy";
 import locale from "$stores/locale";
+import { get } from "svelte/store";
 import Skeleton from "$lib/Loading/Skeleton.svelte";
 import Message from "$lib/Loading/Message.svelte";
 import Dropdown from "$lib/Layout/Dropdown.svelte";
@@ -209,7 +210,9 @@ type GroupedBadges = { [key: string]: IndexedBadge[] };
 
 const setShadowHide = () => {
 	if (!badger) {
-		loadError = "Something went wrong. Try refreshing.";
+		loadError =
+			get(locale)().badgeWall?.page?.somethingWentWrong ??
+			"Something went wrong. Try refreshing.";
 
 		return;
 	}
@@ -547,7 +550,9 @@ const shadowHideBadge = () => {
 	if (!selectedBadge && !authorised) return;
 
 	if (!badger) {
-		loadError = "Something went wrong. Try refreshing.";
+		loadError =
+			get(locale)().badgeWall?.page?.somethingWentWrong ??
+			"Something went wrong. Try refreshing.";
 
 		return;
 	}
@@ -561,7 +566,10 @@ const shadowHideBadge = () => {
 };
 </script>
 
-<HeadTitle route={`${data.username}'s Badge Wall`} path={`/user/${data.username}`} />
+<HeadTitle
+  route={$locale({ values: { username: data.username } }).headTitle?.userBadgeWall}
+  path={`/user/${data.username}`}
+/>
 
 {#if loadError}
   <Popup fullscreen locked>
@@ -571,11 +579,11 @@ const shadowHideBadge = () => {
   {@const isOwner = $identity && (isId ? $identity.id : $identity.name) === data.username}
 
   {#if $BadgeWallUser.fetching || !$BadgeWallUser.data}
-    <Message message="Loading badges ..." />
+    <Message message={$locale().badgeWall?.page?.loadingBadges} />
 
     <Skeleton grid={true} count={100} width="150px" height="170px" />
   {:else if !$BadgeWallUser.data.User}
-    <Message message="No badges yet." />
+    <Message message={$locale().badgeWall?.page?.noBadgesYet} />
   {:else}
     {@const ungroupedBadges = castBadgesToIndexedBadges($BadgeWallUser.data.User.badges)}
     {@const isBadgeSelected =
@@ -591,7 +599,7 @@ const shadowHideBadge = () => {
       {/if}
 
       {#if ungroupedBadges === null}
-        <Message message="Loading badges ..." />
+        <Message message={$locale().badgeWall?.page?.loadingBadges} />
 
         <Skeleton grid={true} count={10} width="150px" height="170px" />
       {:else}
@@ -605,23 +613,17 @@ const shadowHideBadge = () => {
 
           {#if shadowHidden}
             <div class="card">
-              <b>Notice:</b> The Badge Wall overseer system has detected badges containing
-              AI-generated material on your wall. {shadowHiddenCount} of your badges have been shadow
-              hidden.
+              <b>{$locale().badgeWall?.page?.notice}</b>
+              {$locale({ values: { count: shadowHiddenCount } }).badgeWall?.page?.shadowHideNotice1}
               <Spacer />
-              You may use the "Un-shadow Hide Badges" button to unhide these badges, from where you will
-              be required to use the hide feature to hide these badges from the public, while allowing
-              them to stay visible to you as the account holder.
+              {$locale().badgeWall?.page?.shadowHideNotice2}
             </div>
           {:else if false && !noticeDismissed}
             <div class="card">
-              <b>Notice:</b> AniList has begun purging outbound links which contain AI-generated
-              material, this includes Badge Wall. If you have collected badges with AI-generated
-              elements, kindly use the hide feature to hide these badges from the public, while
-              allowing them to stay visible to you as the account holder.
+              <b>{$locale().badgeWall?.page?.notice}</b>
+              {$locale().badgeWall?.page?.aiNotice1}
               <Spacer />
-              Failure to comply with this request at your earliest convenience will result in the hiding
-              of all badges from your Badge Wall.
+              {$locale().badgeWall?.page?.aiNotice2}
               <Spacer />
               <button
                 onclick={async () => {
@@ -630,7 +632,7 @@ const shadowHideBadge = () => {
                   await localforage.setItem('badgeWallNoticeDismissed', 'true');
                 }}
               >
-                Dismiss
+                {$locale().badgeWall?.page?.dismiss}
               </button>
             </div>
           {/if}
@@ -639,7 +641,7 @@ const shadowHideBadge = () => {
 
           <div class="card">
             {#if authorised}
-              <button onclick={setShadowHide}>Shadow Hide Badges</button>
+              <button onclick={setShadowHide}>{$locale().badgeWall?.page?.shadowHide}</button>
             {/if}
 
             {#if isOwner && authorised}
@@ -675,7 +677,7 @@ const shadowHideBadge = () => {
                   migrateMode = !migrateMode;
                 }}
               >
-                Migrate Category
+                {$locale().badgeWall?.page?.migrateCategory}
               </button>
               <span style="margin: 0 0.625rem;">•</span>
               <button
@@ -684,14 +686,14 @@ const shadowHideBadge = () => {
                   hideMode = !hideMode;
                 }}
               >
-                Hide Category
+                {$locale().badgeWall?.page?.hideCategory}
               </button>
               <!-- <!-- <span style="margin: 0 0.625rem;">•</span> -->
               <!-- <button onclick={() => exportBadges(groupedBadges)}>Export Badges</button> -->
 
               {#if shadowHidden}
                 <span style="margin: 0 0.625rem;">•</span>
-                <button onclick={setShadowHide}>Un-shadow Hide Badges</button>
+                <button onclick={setShadowHide}>{$locale().badgeWall?.page?.unshadowHide}</button>
               {/if}
 
               {#if editMode && isOwner}
@@ -784,7 +786,7 @@ const shadowHideBadge = () => {
                       ? dateToInputTime(databaseTimeToDate(selectedBadge.time))
                       : ''}
                   />
-                  <small>Must be full date and time, defaults to now if any fields empty</small>
+                  <small>{$locale().badgeWall?.page?.dateTimeHint}</small>
                 </span>
 
                 <Spacer />
@@ -827,13 +829,17 @@ const shadowHideBadge = () => {
                   </Dropdown>
                   <Dropdown
                     items={[false, true].map((hidden) => ({
-                      name: hidden ? 'Hidden' : 'Shown',
+                      name: hidden
+                        ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+                        : ($locale().badgeWall?.page?.shown ?? 'Shown'),
                       url: '#',
                       onClick: () => {
                         const hiddenInput = document.querySelector('input[name="hidden"]');
 
                         if (hiddenInput instanceof HTMLInputElement)
-                          hiddenInput.value = hidden ? 'Hidden' : 'Shown';
+                          hiddenInput.value = hidden
+                            ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+                            : ($locale().badgeWall?.page?.shown ?? 'Shown');
                       }
                     }))}
                     header={false}
@@ -842,16 +848,16 @@ const shadowHideBadge = () => {
                     <span slot="title">
                       <input
                         type="text"
-                        placeholder="Shown"
+                        placeholder={$locale().badgeWall?.page?.shown}
                         name="hidden"
                         minlength="1"
                         maxlength="1000"
                         size="15"
                         value={selectedBadge
                           ? selectedBadge.hidden
-                            ? 'Hidden'
-                            : 'Shown'
-                          : 'Shown'}
+                            ? ($locale().badgeWall?.page?.hidden ?? 'Hidden')
+                            : ($locale().badgeWall?.page?.shown ?? 'Shown')
+                          : ($locale().badgeWall?.page?.shown ?? 'Shown')}
                       />
                     </span>
                   </Dropdown>
@@ -879,9 +885,11 @@ const shadowHideBadge = () => {
 
         {#if loadQueryParameter === 'none'}
           <div class="card">
-            <b>Notice:</b>
-            {ungroupedBadges.length} badges have been loaded successfully, but they are not being displayed
-            due to your preferences (<code>load=none</code>).
+            <b>{$locale().badgeWall?.page?.notice}</b>
+            {$locale({
+              values: { count: ungroupedBadges.length }
+            }).badgeWall?.page?.loadNoneNoticePrefix}<code>load=none</code>{$locale().badgeWall
+              ?.page?.loadNoneNoticeSuffix}
           </div>
         {:else}
           <Badges
@@ -919,10 +927,12 @@ const shadowHideBadge = () => {
         {#if authorised}
           <button onclick={shadowHideBadge}>
             {#if selectedBadge && selectedBadge.shadow_hidden}
-              Un-shadow
+              {$locale({ values: { id: selectedBadge.id } }).badgeWall?.page?.unshadowHideBadge}
             {:else}
-              Shadow
-            {/if} Hide Badge ({selectedBadge ? selectedBadge.id : 0})
+              {$locale({
+                values: { id: selectedBadge ? selectedBadge.id : 0 }
+              }).badgeWall?.page?.shadowHideBadge}
+            {/if}
           </button>
         {/if}
       </Popup>
@@ -1026,13 +1036,13 @@ const shadowHideBadge = () => {
 {/if}
 
 <Popup fullscreen onLeave={() => (migrateMode = false)} show={migrateMode}>
-  Migrate Category
+  {$locale().badgeWall?.page?.migrateCategory}
 
   <Spacer />
 
   <input
     type="text"
-    placeholder="Original Category"
+    placeholder={$locale().badgeWall?.page?.originalCategoryPlaceholder}
     id="migrate_original"
     minlength="1"
     maxlength="1000"
@@ -1040,13 +1050,13 @@ const shadowHideBadge = () => {
   />
   <input
     type="text"
-    placeholder="New Category"
+    placeholder={$locale().badgeWall?.page?.newCategoryPlaceholder}
     id="migrate_new"
     minlength="1"
     maxlength="1000"
     size="20"
   />
-  <SettingHint lineBreak>Leave category empty to migrate all to or from uncategorised.</SettingHint>
+  <SettingHint lineBreak>{$locale().badgeWall?.page?.migrateAllHint}</SettingHint>
 
   <Spacer />
 
@@ -1060,29 +1070,28 @@ const shadowHideBadge = () => {
     {$locale().user.badges.importMode.cancel}
   </button>
   <button onclick={() => migrateCategory()} class="button-lined" style="float: right;">
-    Migrate
+    {$locale().badgeWall?.page?.migrateAction}
   </button>
 </Popup>
 
 <Popup fullscreen onLeave={() => (hideMode = false)} show={hideMode}>
-  Hide Category
+  {$locale().badgeWall?.page?.hideCategory}
 
   <SettingHint lineBreak>
-    If the majority of the badges in a category are shown, the category will be hidden, and vice
-    versa.
+    {$locale().badgeWall?.page?.hideVisibilityHint}
   </SettingHint>
 
   <Spacer />
 
   <input
     type="text"
-    placeholder="Category"
+    placeholder={$locale().badgeWall?.page?.categoryPlaceholder}
     id="category_hide"
     minlength="1"
     maxlength="1000"
     size="20"
   />
-  <SettingHint lineBreak>Leave category field empty to hide all.</SettingHint>
+  <SettingHint lineBreak>{$locale().badgeWall?.page?.hideAllHint}</SettingHint>
 
   <Spacer />
 
@@ -1096,6 +1105,6 @@ const shadowHideBadge = () => {
     {$locale().user.badges.importMode.cancel}
   </button>
   <button onclick={() => hideCategory()} class="button-lined" style="float: right;"
-    >Toggle Visibility</button
+    >{$locale().badgeWall?.page?.toggleVisibility}</button
   >
 </Popup>
-- 
cgit v1.2.3

